import { Button, Dropdown, Select, Menu, Popconfirm } from 'antd'
import { CloseOutlined, SubnodeOutlined, SisternodeOutlined } from '@ant-design/icons'
import * as React from 'react'
import { Expression, ExpressionLineProps, ExpressionProps, Field, FieldType, I18N, Operator, RelationType } from './interface'
import { operator_map, field_allow_operator, OperatorKey } from './operators'
export * from './interface'
export * from './operators'

export const updateExpression = function update (obj: Expression<any>, paths: string[], value: any) {
    if (paths.length === 0) return value
    if (!obj) return null
    const [key, ...rest] = paths
    if (Array.isArray(obj)) {
        let index = Number(key)
        if (index in obj) {
            if (rest.length === 0 && value == null) {
                return obj.filter((o, i) => i != index)
            } else {
                return obj.map((o, i) => {
                    if (i === index) {
                        return update(o, rest, value)
                    } else {
                        return o
                    }
                })
            }
        } else {
            return obj.concat(value)
        }
    } else {
        return {
            ...obj,
            [key]: update(obj[key], rest, value),
        }
    }
}

export const ExpressionLine = (props: ExpressionLineProps) => {
    const { fields, type_map, op_map, value, onChange, onDelete } = props
    let field = fields.find(f => f.name === value.field)
    let operators = type_map[field?.type]?.map(t => op_map[t]) || []
    const operator = operators.find(op => op.key === value.op) || operators[0]
    return <div className="expression-line">
        <div className="expression-field">
            <Select value={value.field} onChange={field => {
                const _field = fields.find(f => f.name === field)
                onChange({ field, op: type_map[_field?.type][0], value: null })
            }} optionFilterProp="label">
                {fields.map(f => <Select.Option key={f.name} value={f.name} label={f.title || f.name}>
                    {f.title || f.name}
                </Select.Option>)}
            </Select>
        </div>
        {operator.label && <div className="expression-operator">
            <Select value={value.op} onChange={op => onChange({ ...value, op, value: null })}>
                {operators.map(f => <Select.Option key={f.key} value={f.key} label={f.label} title={f.title}>
                    {f.label}
                </Select.Option>)}
            </Select>
        </div>}
        
        <div className="expression-value">
            {operator?.component && <operator.component fields={fields} fieldName={value.field} value={value.value} onChange={v => onChange({ ...value, value: v })}/>}
        </div>

        <div className="expression-other">
            <Button type="link" onClick={onDelete}><CloseOutlined/></Button>
        </div>
    </div>
}

const ExpressionCreator = <T extends React.Key = OperatorKey, F extends React.Key = FieldType>(props: ExpressionProps<T, F>) => {
    const {
        fields,
        op_map = operator_map as unknown as Record<T, Operator<T>>,
        type_map = field_allow_operator as unknown as Record<F, T[]>,
        value,
        deepth = 2,
        i18n = I18N,
    } = props

    const defaultField = fields[0]
    const defaultOperators = type_map[defaultField.type]
    const onChange = (paths: string[]) => (v: any) => {
        props.onChange(updateExpression(value, paths, v))
    }
    const onDelete = (paths: string[]) => () => {
        props.onChange(updateExpression(value, paths, null))
    }

    const addExpressionItem = (paths: string[]) => () => {
        props.onChange(updateExpression(value, paths, {
            field: defaultField.name,
            op: defaultOperators[0],
            value: null,
        }))
    }
    const addExpressionGroup = (paths: string[]) => () => {
        props.onChange(updateExpression(value, paths, {
            relation: RelationType.AND,
            items: [
                {
                    field: defaultField.name,
                    op: defaultOperators[0],
                    value: null,
                }
            ]
        }))
    }
    const loop_render = function loop_render(exp: Expression<any>, paths: string[] = []) {
        if (!exp) return null
        if ('items' in exp) {
            return <React.Fragment>
                {exp.items.length > 0 && <div className={"expression-inner rel-" + (exp.relation === RelationType.AND ? 'and' : 'or')}>
                    <div className="inner-border"
                        onClick={() => onChange(paths.concat('relation'))(
                            exp.relation === RelationType.AND ? RelationType.OR : RelationType.AND
                        )}
                    ></div>
                    {exp.items.map((item, i) => {
                        let new_paths = paths.concat(['items', `${i}`])
                        if ('field' in item) {
                            return <ExpressionLine key={i}
                                fields={fields}
                                op_map={op_map}
                                type_map={type_map}
                                value={item}
                                onChange={onChange(new_paths)}
                                onDelete={onDelete(exp.items.length === 1 ? paths : new_paths)}/>
                        } else {
                            return <React.Fragment key={i}>
                                {loop_render(item, new_paths)}
                            </React.Fragment>
                        }
                    })}
                </div>}
                <div className="expression-handler">
                    {paths.length < (deepth - 1) * 2
                    ?
                        <Dropdown menu={{
                            items: [
                                {
                                    key: 0,
                                    onClick: addExpressionItem(paths.concat('items').concat(exp.items.length + '')),
                                    label: i18n.exp_item,
                                    icon: <SubnodeOutlined />,
                                },
                                {
                                    key: 1,
                                    onClick: addExpressionGroup(paths.concat('items').concat(exp.items.length + '')),
                                    label: i18n.exp_group,
                                    icon: <SisternodeOutlined />,
                                },
                            ]
                        }}>
                            <Button type="link" size="small" onClick={addExpressionItem(paths.concat('items').concat(exp.items.length + ''))}>{i18n.op_add}</Button>
                        </Dropdown>
                    :
                        <Button type="link" size="small" onClick={addExpressionItem(paths.concat('items').concat(exp.items.length + ''))}>{i18n.op_add}</Button>
                    }
                    
                    <Popconfirm title={i18n.op_del_confirm.replace('{}', i18n.exp_group)} onConfirm={onDelete(paths)}>
                        <Button type="link" size="small" className="handler-del">
                            {i18n.op_del}
                        </Button>
                    </Popconfirm>
                </div>
            </React.Fragment>
        } else {
            return <ExpressionLine
                fields={fields}
                op_map={op_map}
                type_map={type_map}
                value={exp}
                onChange={onChange(paths)}
                onDelete={onDelete(paths)}/>
        }
    }

    return <div className="expression-creator">
        {loop_render(value)}
        {!value && <div className="expression-handler">
            <Dropdown menu={{
                items: [
                    {
                        key: 0,
                        onClick: addExpressionItem([]),
                        icon: <SubnodeOutlined />,
                        label: i18n.exp_item,
                    },
                    {
                        key: 1,
                        onClick: addExpressionGroup([]),
                        icon: <SisternodeOutlined />,
                        label: i18n.exp_group,
                    },
                ]
            }}>
                <Button type="link" size="small">{i18n.op_add}</Button>
            </Dropdown>
        </div>}
        
    </div>
}

export default ExpressionCreator

export const ExpressionProvider = <T extends React.Key = OperatorKey>({
    field = 'field', op = 'op', value = 'value', relation = 'relation', items = 'items'
}: Partial<Record<'field' | 'op' | 'value' | 'relation' | 'items', string>>) => {
    return {
        toExpression: function parse (exp: any): Expression<T> {
            if (!exp) return exp
            if (relation in exp) {
                return {
                    relation: exp[relation],
                    items: exp[items] && exp[items].map(parse),
                }
            }
            return {
                field: exp[field],
                op: exp[op],
                value: exp[value],
            }
        },
        toValue: function parse (exp: Expression<T>) {
            if (!exp) return exp
            if ('relation' in exp) {
                return {
                    [relation]: exp.relation,
                    [items]: exp.items && exp.items.map(parse),
                }
            }
            return {
                [field]: exp.field,
                [op]: exp.op,
                [value]: exp.value,
            }
        }
    }
}